﻿namespace System.ServiceModel.DomainServices.Client
{
    using System;
    using System.Threading;

    internal abstract class AsyncResultBase : IAsyncResult
    {
        private readonly object _asyncState;
        private readonly AsyncCallback _callback;
        private bool _endMethodInvoked;
        private IAsyncResult _innerAsyncResult;
        private bool _isCancellationRequested;
        private bool _isCompleted;
        private readonly object _syncLock = new object();

        public AsyncResultBase(AsyncCallback callback, object asyncState)
        {
            this._callback = callback;
            this._asyncState = asyncState;
        }

        public virtual void Cancel()
        {
            lock (this._syncLock)
            {
                if (!this._endMethodInvoked)
                {
                    this._isCancellationRequested = true;
                }
            }
        }

        public void Complete()
        {
            lock (this._syncLock)
            {
                if (this._isCompleted)
                {
                    throw new InvalidOperationException(Resources.MethodCanOnlyBeInvokedOnce);
                }
                this._isCompleted = true;
            }
            if (this._callback != null)
            {
                this._callback(this);
            }
        }

        public static TAsyncResult EndAsyncOperation<TAsyncResult>(IAsyncResult asyncResult) where TAsyncResult: AsyncResultBase
        {
            return EndAsyncOperation<TAsyncResult>(asyncResult, false);
        }

        public static TAsyncResult EndAsyncOperation<TAsyncResult>(IAsyncResult asyncResult, bool cancel) where TAsyncResult: AsyncResultBase
        {
            if (asyncResult == null)
            {
                throw new ArgumentNullException("asyncResult");
            }
            TAsyncResult local = asyncResult as TAsyncResult;
            if (local == null)
            {
                throw new ArgumentException(Resources.WrongAsyncResult, "asyncResult");
            }
            if (local.CancellationRequested)
            {
                throw new InvalidOperationException(Resources.OperationCancelled);
            }
            if (cancel)
            {
                local.Cancel();
            }
            else if (!local.IsCompleted)
            {
                throw new InvalidOperationException(Resources.OperationNotComplete);
            }
            local.SetEndMethodInvokedFlag();
            return local;
        }

        private void SetEndMethodInvokedFlag()
        {
            lock (this._syncLock)
            {
                if (this._endMethodInvoked)
                {
                    throw new InvalidOperationException(Resources.MethodCanOnlyBeInvokedOnce);
                }
                this._endMethodInvoked = true;
            }
        }

        public object AsyncState
        {
            get
            {
                return this._asyncState;
            }
        }

        public WaitHandle AsyncWaitHandle
        {
            get
            {
                throw new NotSupportedException();
            }
        }

        public bool CancellationRequested
        {
            get
            {
                return this._isCancellationRequested;
            }
        }

        public bool CompletedSynchronously
        {
            get
            {
                return false;
            }
        }

        public IAsyncResult InnerAsyncResult
        {
            get
            {
                return this._innerAsyncResult;
            }
            set
            {
                this._innerAsyncResult = value;
            }
        }

        public bool IsCompleted
        {
            get
            {
                return this._isCompleted;
            }
        }
    }
}

